package 反射;

import java.lang.reflect.Method;

synthetic--合成

读完这篇文章你将会收获到

 -synthetic fields
 -synthetic method
 -synthetic class

概述
上一篇 Java 枚举 提及到编译成 class文件之后、编译器会在枚举类里帮我们生成一个 VALUES的静态数组 , 这种编译器生成的都有一个 flag叫做 synthetic

▼fields
 ▶#0:...
 ▶#1:...
 ▼#2:$VALUES
   access_flags:ACC_PRIVATE,ACC_STATIC,ACC_FINAL,ACC_SYNTHETIC
   name_index:#35->$VALUES
   descriptor_index:#36->[Lother/BehaviorEnum;
   attributes_count:0

那么 synthetic的定义是什么、什么情况下才会有这个 flag?

synthetic的定义:
	由编译器生成的，在源代码中没有出现的，都会被标记为synthetic。当然有一些例外的情况:默认的构造函数、类的初始化方法、以及枚举类中的value和valueOf方法。

《synthetic fields》

非常常见的一个例子：

public class Father {
	class Son {
	}
}

我们都知道在一个内部类中，可以直接访问外部类的属性和方法，因为在内部类中是存在一个外部类的一个引用变量，而这个引用变量即是编译器帮我们生成的、也就是一个synthetic的属性

▼fields
 ▼#0:this$0
   access_flags:ACC_FINAL,ACC_SYNTHETIC
   name_index:#5->this$0
   descriptor_index:#6->Lcom/deme/data/Father;
   attributes_count:0
   attributes

我们再写一个测试来验证下

Class<Father.Son> clazz = Father.Son.class;
Field[] declaredFields = clazz.getDeclaredFields();

for (Field declaredField : declaredFields) {
   System.out.println(declaredField.getName() + ":" + declaredField.isSynthetic());
}

this$0:true

我们再来验证一下上一篇文章 Java 枚举的枚举类

Class<BehaviorEnum> clazz = BehaviorEnum.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
   System.out.println(declaredField.getName() + ":" + declaredField.isSynthetic());
}

FOLLOW:false
WOW:false
FORWARD_TO_FRIENDS:false
ADD_TO_FAVORITES:false
$VALUES:true

synthetic method
我们再来看看被 synthetic修饰的方法吧

public class Father {
   class Son {
      private String name;
   }

   /**
    * just for test synthetic
    * @return
    */
   public String getName() {
      // just for test synthetic
      return new Son().name;
   }
}



在Son类中突然多出了这么一个方法。因为其实 name属性是一个私有方法、外部类 Father中却能直接访问这个属性、对于我们写代码来说、这是非常合理的一个事情、但是这都是编译
器默默的付出、为我们生成了一个静态的 package范围的方法、参数就是 Son的实例、返回值就是 String

Class<Father.Son> sonClass = Father.Son.class;
Method[] declaredMethods = sonClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
    System.out.println(
            Modifier.toString(declaredMethod.getModifiers()) +
                    ":" + declaredMethod.getName() +
                    ":" + declaredMethod.isSynthetic());

}

static:access$000:true

synthetic class
我们再来看看被 synthetic修饰的 class

public class Father03 {
    public Son generateSon() {
        return new Son();
    }
    private class Son{
    }
}

然后我们编译为 class文件



发现多出来一个匿名类 Father03$1.class这个是什么鬼鬼



这个类完全是一个空的类、父类直接是 Object、也没额外定义一些自己的方法

我们再看看 Father03$Son.class



发现它居然有两个构造方法，一个带参数的 package scope 的构造参数是编译器生成的。参数是 Father03和 Father03$1

我们再看看 Father03里面的 generateSon方法



发现它调用的是那个带参数的构造方法，并且参数 Father03$1的值是为 null的

根据上面的种种信息来看、我们可以这么认为、对于一个 private的内部类(其构造函数默认也是 private) , 外部类也是无法直接去创建它的实例的、其实换句话来说、对于类的定义来说、不管你是作为一个内部类定义在另一个类中、还是单独定义在一个 java文件，java的可见性都是起效的。至于为啥可以在外部内直接创建一个 private 的类的实例、无外乎就是 java 编译器帮我们做了一些额外的工作。

回到上面的例子中、因为 Father03$Son. 只有一个私有的构造函数、而为了能在 Father03中去创建这么一个 Father03$Son对象，编译器不得不为我们生成一个 package scope的构造函数、而午餐的构造函数已经存在了、那编译器只能创建一个有参的构造函数啊、那么问题来了、这个参数的类型应该是啥、那就生成一个类呗、专门为这个参数用。而调用这个构造函数的时候、就直接传给 null值给它

所以说 Father03$1作用无外乎可能就是作为一个参数的类型被用到





